// WebSocket连接管理器
// 提供WebSocket连接、消息发送、重连、心跳检测等功能

import type {
  WebSocketOptions,
  WebSocketMessage,
  ConnectionStatus
} from '../types/webSocket'

/**
 * WebSocket管理器类
 * 负责管理WebSocket连接、消息发送与接收、自动重连和心跳检测
 */
export class WebSocketManager {
  private ws: WebSocket | null = null // WebSocket实例
  private reconnectAttempts = 0 // 重连尝试次数
  private heartbeatTimer: number | null = null // 心跳计时器ID
  private isManualClose = false // 是否手动关闭连接
  private userId: string | null = null // 用户ID

  // 公共状态
  public status: ConnectionStatus = '未连接' // 当前连接状态

  /**
   * 构造函数
   * @param options WebSocket配置选项
   */
  constructor(private options: WebSocketOptions) {
    this.options = {
      maxReconnectAttempts: 5, // 默认最大重连次数
      reconnectInterval: 2000, // 默认重连间隔（毫秒）
      heartbeatInterval: 20000, // 默认心跳检测间隔（毫秒）
      ...options // 合并用户提供的配置
    }
  }

  /**
   * 连接WebSocket服务器
   * 如果已经处于连接状态，则不会重复连接
   * @returns {void}
   */
  public connect(userId: string): void {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      return
    }
    this.isManualClose = false
    this.updateStatus('连接中')
    try {
      console.log('尝试连接WebSocket:', this.options.url)
      this.userId = userId
      this.ws = new WebSocket(`${this.options.url}/chat?userId=${this.userId}`)
      this.setupEventListeners()
    } catch (error) {
      this.updateStatus('连接失败')
      console.error(
        `创建连接失败: ${error instanceof Error ? error.message : '未知错误'}`
      )
      this.options.onError?.(error as Event)
    }
  }

  /**
   * 设置WebSocket事件监听器
   * @private
   * @returns {void}
   */
  private setupEventListeners(): void {
    if (!this.ws) return

    /**
     * 处理WebSocket连接打开事件
     * 更新连接状态、重置重连尝试次数、添加系统消息、启动心跳检测
     * @returns {void}
     */
    this.ws.onopen = (): void => {
      this.updateStatus('已连接')
      this.reconnectAttempts = 0
      console.log('WebSocket连接已建立')
      this.startHeartbeat()
    }

    /**
     * 处理收到的消息事件
     * 尝试解析JSON消息，失败时直接显示原始内容
     * @param event MessageEvent 包含消息数据的事件对象
     * @returns {void}
     */
    this.ws.onmessage = (event: MessageEvent): void => {
      try {
        const message: WebSocketMessage = JSON.parse(event.data)
        console.log('websocket.ts -> 收到消息:', message)
        this.options.onMessage?.(message)
      } catch (error) {
        console.warn('无法解析消息:', event.data, error)
      }
    }

    /**
     * 处理WebSocket连接关闭事件
     * 清理资源、更新连接状态、添加系统消息、根据配置判断是否重连
     * @param event CloseEvent 包含关闭事件信息的对象
     * @returns {void}
     */
    this.ws.onclose = (event: CloseEvent): void => {
      this.cleanup()
      this.updateStatus('已断开')
      console.log(`WebSocket连接已关闭: ${event.code}`)

      // 非手动关闭且需要重连
      if (
        !this.isManualClose &&
        this.reconnectAttempts < (this.options.maxReconnectAttempts || 5)
      ) {
        this.attemptReconnect()
      }
    }

    /**
     * 处理WebSocket连接错误事件
     * 更新连接状态、添加系统消息、调用错误回调
     * @param error Event 包含错误信息的事件对象
     * @returns {void}
     */
    this.ws.onerror = (error: Event): void => {
      this.updateStatus('连接错误')
      console.error('WebSocket连接发生错误')
      this.options.onError?.(error)
    }
  }

  /**
   * 尝试重新连接WebSocket服务器
   * 使用指数退避策略增加重连间隔
   * @private
   * @returns {void}
   */
  private attemptReconnect(): void {
    this.reconnectAttempts++
    const delay =
      (this.options.reconnectInterval || 2000) * this.reconnectAttempts // 指数退避

    console.log(`尝试第 ${this.reconnectAttempts} 次重连...`)

    setTimeout(() => {
      if (!this.isManualClose) {
        if (this.userId) {
          this.connect(this.userId)
        }
      }
    }, delay)
  }

  /**
   * 发送WebSocket消息
   * @param message 要发送的消息对象
   * @returns {boolean} 发送是否成功
   */
  public sendMessage(message: WebSocketMessage): boolean {
    console.log('websocket.ts -> 发送消息')

    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
      console.warn('WebSocket未连接，无法发送消息')
      return false
    }

    try {
      // 添加时间戳到消息
      const messageWithTimestamp = {
        ...message,
        timestamp: new Date().toISOString().slice(0, 19) // 移除毫秒和时区
      }
      // 发送消息
      this.ws.send(JSON.stringify(messageWithTimestamp))
      return true
    } catch (error) {
      console.error(
        `发送消息失败: ${error instanceof Error ? error.message : '未知错误'}`
      )
      return false
    }
  }

  /**
   * 主动断开WebSocket连接
   * 标记为手动关闭，避免自动重连
   * @returns {void}
   */
  public disconnect(): void {
    this.isManualClose = true
    this.cleanup()

    if (this.ws) {
      this.ws.close(1000, '用户主动关闭')
    }
  }

  /**
   * 清理WebSocket资源
   * 停止心跳检测并更新连接状态
   * @private
   * @returns {void}
   */
  private cleanup(): void {
    this.stopHeartbeat()
    this.updateStatus('已断开')
  }

  /**
   * 启动心跳检测机制
   * 定期发送心跳消息以保持连接活跃
   * 在Electron环境中确保使用正确的定时器
   * @private
   * @returns {void}
   */
  private startHeartbeat(): void {
    this.stopHeartbeat() // 先停止已有的心跳检测

    // 使用安全的setInterval，确保在Electron渲染进程中正常工作
    const safeSetInterval =
      typeof window !== 'undefined' && window.setInterval
        ? window.setInterval
        : setInterval

    this.heartbeatTimer = Number(
      safeSetInterval(() => {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
          this.ws.send(
            JSON.stringify({
              type: 'heartbeat',
              timestamp: Date.now()
            })
          )
        }
      }, this.options.heartbeatInterval)
    )
  }

  /**
   * 停止心跳检测
   * @private
   * @returns {void}
   */
  private stopHeartbeat(): void {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
  }

  /**
   * 更新WebSocket连接状态
   * 并通知注册的状态变化回调函数
   * @private
   * @param status 新的连接状态
   * @returns {void}
   */
  private updateStatus(status: ConnectionStatus): void {
    this.status = status
    this.options.onStatusChange?.(status)
  }

  /**
   * 获取当前WebSocket连接的readyState
   * @returns {number} WebSocket的readyState值
   */
  public getReadyState(): number {
    return this.ws?.readyState || WebSocket.CLOSED
  }

  /**
   * 销毁WebSocketManager实例
   * 断开连接并释放资源
   * @returns {void}
   */
  public destroy(): void {
    this.disconnect()
    this.ws = null
  }
}
